package edu.poly.game.board.impl;

import java.util.ArrayList;
import java.util.List;

import edu.poly.game.battleship.BattleShip;
import edu.poly.game.board.GameBoard;
import edu.poly.game.player.MissileFireResult;
import edu.poly.game.player.MissileFireResultType;
import edu.poly.game.util.BoundaryValidationUtil;
import edu.poly.game.util.Coordinate;

public class SquareGameBoard implements GameBoard {
	
	//game board width, this is a square board
	private int width;
	
	//variable for convenience, represents the starting point of this board, should be (1, 1)
	private Coordinate fromCoordinate;
	//represents the ending point of this board, should be (width, width)
	private Coordinate toCoordinate;
	
	//actual grid
	private char[][] grid;
	
	//battle ships associated with this board
	private List<BattleShip> battleShips;
	//for convenience for checking if there's ships left, instead of iterating through the ship list
	//performance related, initialized to 0
	private int numberOfBattleShips = 0;
	
	public SquareGameBoard(final int width) {
		this.width = width;
		this.fromCoordinate = new Coordinate(0, 0);
		this.toCoordinate = new Coordinate(width - 1, width - 1);
		
		this.grid = new char[width][width];
		for(int x = 0; x < grid.length; x++) {
			for(int y = 0; y < grid[x].length; y++) {
				this.grid[x][y] = '*';
			}
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getWidth() {
		return this.width;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getHeight() {
		return this.width;
	}
	
	private boolean isWithinBoardRange(final Coordinate coordinate) {
		if(coordinate.getXCoordinate() >= this.width ||
				coordinate.getYCoordinate() >= this.width) {
			return false;
		}
		return true;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean addBattleShip(final BattleShip battleShip) {
		//when adding, take battleship's fromCoordinate and toCoordinate
		//1. make sure these two points does not lie within any ship
		//2. make sure these two points is within board's boundary
		
		if(this.battleShips == null) {
			this.battleShips = new ArrayList<BattleShip>();
		}
		
		if(!this.isWithinBoardRange(battleShip.getFromCoordinate()) || 
				!this.isWithinBoardRange(battleShip.getToCoordinate())) {
			return false;
		}
		
		for(BattleShip ship : this.battleShips) {
			if(!BoundaryValidationUtil.isWithinBoundary(ship.getFromCoordinate(), ship.getToCoordinate(), 
					                                   battleShip.getFromCoordinate(), battleShip.getToCoordinate())) {
				return false;
			}
		}
		
		this.battleShips.add(battleShip);
		//if added successfully, increment numberOfBattleShips
		this.numberOfBattleShips++;		
		//change grid to place this ship on the board, for display purpose
		this.updateGrid(battleShip, '#');

		return true;
	}
	
	private void updateGrid(final BattleShip battleShip, char gridChar) {
		int fromX = battleShip.getFromCoordinate().getXCoordinate();
		int fromY = battleShip.getFromCoordinate().getYCoordinate();
		int toX = battleShip.getToCoordinate().getXCoordinate();
		int toY = battleShip.getToCoordinate().getYCoordinate();
		
		for(int x = fromX; x <= toX; x++) {
			for(int y = fromY; y <= toY; y++) {
				this.grid[x][y] = gridChar;
			}
		}
	}
	
	private void updateGrid(final Coordinate coordinate, char gridChar) {
		this.grid[coordinate.getXCoordinate()][coordinate.getYCoordinate()] = gridChar;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public MissileFireResult receiveMissileHit(final Coordinate missileCoordinate) {
		if(BoundaryValidationUtil.isWithinBoundary(fromCoordinate, toCoordinate, missileCoordinate)) {
			//check if hit any ship
			if(this.battleShips != null && this.battleShips.size() > 0) {
				boolean hasHit = false;
				MissileFireResultType resultType = null;
				for(BattleShip battleShip : this.battleShips) {
					resultType = battleShip.takeMissileHit(missileCoordinate);
					if(resultType == null)
						continue;
					if(MissileFireResultType.HIT.equals(resultType)) {
						this.numberOfBattleShips--;
						this.updateGrid(battleShip, 'D');
						return MissileFireResult.getResult(resultType, battleShip.getBattleShipType());
					} else if (MissileFireResultType.HITDESTROYEDSHIP.equals(resultType)) {
						return MissileFireResult.getResult(resultType, null);
					}
				}
				//no hit
				resultType = MissileFireResultType.MISSED;
				this.updateGrid(missileCoordinate, 'X');
				return MissileFireResult.getResult(resultType, null);
			}
		}
		return MissileFireResult.getResult(MissileFireResultType.OUTOFBOUNDARY, null);
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean hasShipsActive() {
		return this.numberOfBattleShips != 0;
	}

	@Override
	public char[][] getGameBoard() {
		return this.grid;
	}
}
